package com.platform.core.service.impl;

import com.google.common.collect.Lists;
import com.platform.comm.service.impl.CommonBizImpl;
import com.platform.core.entity.*;
import com.platform.core.repository.SysRoleRepository;
import com.platform.core.repository.SysUserRoleRepository;
import com.platform.comm.dto.DtoMapper;
import com.platform.comm.exceptions.RestApiException;
import com.platform.dto.RoleQo;
import com.platform.core.entity.*;
import com.platform.enums.AccessSubOrg;
import com.platform.enums.RoleType;
import com.platform.core.service.SysDepartService;
import com.platform.core.service.SysUserRoleService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static com.platform.comm.constants.GlobalConstant.BASE_REDIS_CACHE_PREFIX;
import static com.platform.core.service.BaseEntityBiz.ROUTE_SEPARATOR;
import static com.platform.core.service.impl.SysUserServiceImpl.USER_INFO_KEY;

/**
 * Description: 用户角色-Service实现类
 *
 * @author libin
 * Created on 2019/4/26
 **/
@Service
@Log4j2
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class SysUserRoleServiceImpl extends CommonBizImpl<SysUserRoleRepository, SysUserRole, String> implements SysUserRoleService {

    private final QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;
    private final QSysRole qSysRole = QSysRole.sysRole;
    private final SysRoleRepository sysRoleRepository;
    private final DtoMapper dtoMapper;
    private final SysDepartService sysDepartService;
    private final RedisTemplate<String, Object> redisTemplate;

    public SysUserRoleServiceImpl(SysUserRoleRepository baseRepository,
                                  SysRoleRepository sysRoleRepository, DtoMapper dtoMapper,
                                  SysDepartService sysDepartService, RedisTemplate<String, Object> redisTemplate) {
        super(baseRepository);
        this.sysRoleRepository = sysRoleRepository;
        this.sysDepartService = sysDepartService;
        this.dtoMapper = dtoMapper;
        this.redisTemplate = redisTemplate;
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#userId")
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void create(String userId, String roleCode) {
        SysUserRole sysUserRole = new SysUserRole();
        Optional<SysRole> optional = sysRoleRepository.findOne(qSysRole.code.eq(roleCode));

        sysUserRole.setUserId(userId);
        sysUserRole.setRoleId(optional.orElseThrow(() ->
                new RestApiException("Can't find role by roleCode[" + roleCode + "]")).getId());
        sysUserRole.setRoleCode(roleCode);
        saveEntity(sysUserRole);
        clearRedis();
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#userId")
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void create(final String userId, List<String> roleIds) {
        removeByUserId(userId);

        roleIds.forEach(roleId -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(userId);
            sysUserRole.setRoleId(roleId);
            saveEntity(sysUserRole);
        });
        clearRedis();
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#userId")
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void createInRoleInfo(String userId, List<RoleQo> roleInfos) {
        removeByUserId(userId);

        roleInfos.forEach(role -> {
            SysUserRole userRole = new SysUserRole();
            userRole.setUserId(userId);
            userRole.setRoleId(role.getId());
            if (role.getRoleType().equals(RoleType.I)) {
                // 系统角色，添加管理组织。
                userRole.setAccessSubOrg(role.getAccessSubOrg());
                SysDepart depart = sysDepartService.getDepartByUserId(userId);
                if (Objects.nonNull(depart) && StringUtils.isNotEmpty(depart.getDomain())) {
                    userRole.setManageDomain(depart.getDomain());
                } else {
                    userRole.setManageDomain(depart.getId());
                }
            }
            saveEntity(userRole);
        });
        clearRedis();
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#userId")
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void removeByUserId(String userId) {
        repository.deleteInBatch(repository.findAll(qSysUserRole.userId.eq(userId)));
        clearRedis();
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", allEntries = true)
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void removeByRoleId(String roleId) {
        repository.deleteInBatch(repository.findAll(qSysUserRole.roleId.eq(roleId)));
        clearRedis();
    }

    @Override
    public List<RoleQo> findRolesByUserId(String userId) {
        List<String> codes = sysRoleRepository.findAll(qSysUserRole.userId.eq(userId),
                jpqlQuery -> jpqlQuery.leftJoin(qSysUserRole).on(qSysUserRole.roleId.eq(qSysRole.id)))
                .parallelStream()
                .map(SysRole::getRoute).filter(Objects::nonNull)
                .map(route -> StringUtils.split(route, ROUTE_SEPARATOR))
                .flatMap(Arrays::stream)
                .distinct().collect(Collectors.toList());
        return StreamSupport.stream(sysRoleRepository.findAll(qSysRole.code.in(codes)).spliterator(), true)
                .map(sysRole -> dtoMapper.map(sysRole, RoleQo.class)).collect(Collectors.toList());
    }

    @Override
    public List<RoleQo> findRolesByUserIdWithoutParent(String userId) {
        return sysRoleRepository.findAll(qSysUserRole.userId.eq(userId),
                jpqlQuery -> jpqlQuery.leftJoin(qSysUserRole).on(qSysUserRole.roleId.eq(qSysRole.id)))
                .stream()
                .map(sysRole -> dtoMapper.map(sysRole, RoleQo.class))
                .collect(Collectors.toList());
    }

    @Override
    public RoleQo findByUserIdAndRoleId(String userId, String roleId) {
        return repository.findOne(qSysUserRole.userId.eq(userId).and(qSysUserRole.roleId.eq(roleId)))
                .map(sysRole -> dtoMapper.map(sysRole, RoleQo.class))
                .orElse(null);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void batchSave(List<SysUserRole> collect) {
        repository.saveAll(collect);
        clearRedis();
    }

    /**
     * 2020-12-23
     * 用户和角色的关联关系 均为从kafka接收到的数据
     * 故本系统无法维护 是否查看下级数据的配置
     *
     * 所以此方法修改为 默认返回查看下级数据
     *
     * @return
     */
    @Override
    public String checkAccessSubOrg() {
        return AccessSubOrg.Y.getCode();
    }

    @Override
    @Transactional(readOnly = false)
    public void remove(List<SysUserRole> sysUserRoles) {
        sysUserRoles.forEach(sysUserRole -> {
            repository.deleteInBatch(repository.findAll(qSysUserRole.roleCode.eq(sysUserRole.getRoleCode())
                    .and(qSysUserRole.userId.eq(sysUserRole.getUserId()))));
        });
        clearRedis();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(List<SysUserRole> sysUserRoles) {
        sysUserRoles.forEach(sysUserRole -> {
            List<SysUserRole> lists =  Lists.newArrayList(repository.findAll(qSysUserRole.roleId.eq(sysUserRole.getRoleId())
                    .and(qSysUserRole.userId.eq(sysUserRole.getUserId()))));
            if(lists.size() < 1){
                repository.save(sysUserRole);
            }
        });
        clearRedis();
    }

    /**
     * 修改用户信息 清除缓存
     */
    private void clearRedis() {
        if (redisTemplate.hasKey(USER_INFO_KEY)) {
            redisTemplate.delete(USER_INFO_KEY);
        }
    }
}
